package com.hsjk.szwj.module.pay.controller.app.order;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.fastjson.JSONObject;
import com.hsjk.szwj.framework.common.exception.ServiceException;
import com.hsjk.szwj.framework.common.exception.enums.GlobalErrorCodeConstants;
import com.hsjk.szwj.framework.tenant.core.context.TenantContextHolder;
import com.hsjk.szwj.module.pay.constant.PayOrderConstant;
import com.hsjk.szwj.module.pay.controller.AbstractPayOrderController;
import com.hsjk.szwj.module.pay.dal.dataobject.config.ChannelRetMsg;
import com.hsjk.szwj.module.pay.dal.dataobject.config.MchAppConfigContext;
import com.hsjk.szwj.module.pay.dal.dataobject.order.PayOrderDO;
import com.hsjk.szwj.module.pay.dal.dataobject.system.MchAppDO;
import com.hsjk.szwj.module.pay.dal.dataobject.system.MchInfoDO;
import com.hsjk.szwj.module.pay.dal.dataobject.system.MchNotifyRecordDO;
import com.hsjk.szwj.module.pay.exception.ResponseException;
import com.hsjk.szwj.module.pay.service.ConfigContextQueryService;
import com.hsjk.szwj.module.pay.service.IChannelNoticeService;
import com.hsjk.szwj.module.pay.service.IMchAppService;
import com.hsjk.szwj.module.pay.service.IMchInfoService;
import com.hsjk.szwj.module.pay.service.MchNotifyRecordService;
import com.hsjk.szwj.module.pay.service.PayMchNotifyService;
import com.hsjk.szwj.module.pay.service.PayOrderProcessService;
import com.hsjk.szwj.module.pay.service.PayOrderService;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.MutablePair;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;

/**
 * @author chenjiawei
 * @desc
 * @Date 2023/08/24 11:28
 **/
@Tag(name = "应用端 - 支付回调")
@RestController
@RequestMapping("/pay/callback")
@Validated
@Slf4j
public class ChannelNoticeController extends AbstractPayOrderController {
    @Resource
    private PayOrderService payOrderService;
    @Resource
    private ConfigContextQueryService configContextQueryService;
    @Resource
    private PayMchNotifyService payMchNotifyService;
    @Resource
    private PayOrderProcessService payOrderProcessService;
    @Resource
    private IMchInfoService mchInfoService;
    @Resource
    private IMchAppService mchAppService;

    @Resource
    private MchNotifyRecordService mchNotifyRecordService;

    /**
     * 同步通知入口
     * <p>
     * payOrderId 前缀为 ONLYJUMP_，表示直接跳转
     **/
    @RequestMapping(value = {"/return/{ifCode}",
            "/return/{ifCode}/{payOrderId}"})
    public String doReturn(HttpServletRequest request,
                           @PathVariable("ifCode") String ifCode,
                           @PathVariable(value = "payOrderId", required = false) Long urlOrderId) {

        Long payOrderId = null;
        String logPrefix = "进入[" + ifCode + "]支付同步跳转：urlOrderId：["
                + StringUtils.defaultIfEmpty(String.valueOf(urlOrderId), "") + "] ";
        log.info("===== {} =====", logPrefix);

        try {
            // 参数有误
            if (StringUtils.isEmpty(ifCode)) {
                return this.toReturnPage("ifCode is empty");
            }
            //查询支付接口是否存在
            IChannelNoticeService payNotifyService =
                    SpringUtil.getBean(ifCode + "ChannelNoticeService",
                            IChannelNoticeService.class);

            // 支付通道接口实现不存在
            if (payNotifyService == null) {
                log.error("{}, interface not exists ", logPrefix);
                return this.toReturnPage("[" + ifCode + "] interface not exists");
            }

            // 仅做跳转，直接跳转订单的returnUrl
            onlyJump(urlOrderId, logPrefix);

            // 解析订单号 和 请求参数
            MutablePair<Long, Object> mutablePair = payNotifyService.parseParams(request,
                    urlOrderId, IChannelNoticeService.NoticeTypeEnum.DO_RETURN);
            // 解析数据失败， 响应已处理
            if (mutablePair == null) {
                log.error("{}, mutablePair is null ", logPrefix);
                //需要实现类自行抛出ResponseException, 不应该在这抛此异常。
                throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST.getCode(),
                        "解析数据异常！");
            }

            //解析到订单号
            payOrderId = mutablePair.left;
            log.info("{}, 解析数据为：payOrderId:{}, params:{}", logPrefix, payOrderId, mutablePair.getRight());

            if (ObjectUtil.isNotNull(urlOrderId) && !urlOrderId.equals(payOrderId)) {
                log.error("{}, 订单号不匹配. urlOrderId={}, payOrderId={} ", logPrefix, urlOrderId, payOrderId);
                throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST.getCode(),
                        "订单号不匹配！");
            }

            //获取订单号 和 订单数据
            PayOrderDO payOrder = payOrderService.getById(payOrderId);

            // 订单不存在
            if (payOrder == null) {
                log.error("{}, 订单不存在. payOrderId={} ", logPrefix, payOrderId);
                return this.toReturnPage("支付订单不存在");
            }
            MchInfoDO mchInfoDO = mchInfoService.getMchInfoById(payOrder.getMchId());
            MchAppDO mchAppDO = mchAppService.getById(payOrder.getAppId());
            //查询出商户应用的配置信息
            MchAppConfigContext mchAppConfigContext =
                    configContextQueryService.queryMchInfoAndAppInfo(mchInfoDO.getId(),
                            mchAppDO.getId());

            //调起接口的回调判断
            ChannelRetMsg notifyResult = payNotifyService.doNotice(request, mutablePair.getRight(),
                    payOrder, mchAppConfigContext, IChannelNoticeService.NoticeTypeEnum.DO_RETURN);

            // 返回null 表明出现异常， 无需处理通知下游等操作。
            if (notifyResult == null || notifyResult.getChannelState() == null || notifyResult.getResponseEntity() == null) {
                log.error("{}, 处理回调事件异常  notifyResult data error, notifyResult ={} ", logPrefix, notifyResult);
                //需要实现类自行抛出ResponseException, 不应该在这抛此异常。
                throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST.getCode(),
                        "处理回调事件异常！");
            }

            //判断订单状态
            if (notifyResult.getChannelState() == ChannelRetMsg.ChannelState.CONFIRM_SUCCESS) {
                payOrder.setState(PayOrderConstant.STATE_SUCCESS);
            } else if (notifyResult.getChannelState() == ChannelRetMsg.ChannelState.CONFIRM_FAIL) {
                payOrder.setState(PayOrderConstant.STATE_FAIL);
            }

            boolean hasReturnUrl = StringUtils.isNotBlank(payOrder.getReturnUrl());
            log.info("===== {}, 订单通知完成。 payOrderId={}, parseState = {}, hasReturnUrl={} =====", logPrefix, payOrderId, notifyResult.getChannelState(), hasReturnUrl);

            //包含通知地址时
            if (hasReturnUrl) {
                // 重定向
                response.sendRedirect(payMchNotifyService.createReturnUrl(payOrder, mchAppConfigContext.getMchApp().getAppSecret()));
                return null;
            } else {

                //跳转到支付成功页面
                return this.toReturnPage(null);
            }

        } catch (ServiceException e) {
            log.error("{}, payOrderId={}, BizException", logPrefix, payOrderId, e);
            return this.toReturnPage(e.getMessage());

        } catch (ResponseException e) {
            log.error("{}, payOrderId={}, ResponseException", logPrefix, payOrderId, e);
            return this.toReturnPage(e.getMessage());

        } catch (Exception e) {
            log.error("{}, payOrderId={}, 系统异常", logPrefix, payOrderId, e);
            return this.toReturnPage(e.getMessage());
        }
    }

    /**
     * 异步回调入口
     **/
    @ResponseBody
    @RequestMapping(value = {"/{ifCode}", "/{ifCode}/{payOrderId}"})
    public ResponseEntity doNotify(HttpServletRequest request, @PathVariable("ifCode") String ifCode,
                                   @PathVariable(value = "payOrderId", required = false)
                                   Long urlOrderId) {
        log.info("doNotify url:{}", request.getRequestURI());
        String notifyBodyVal = getVerifyNotifyBody(request);
        log.info("doNotify-request-{}", notifyBodyVal);
        Long payOrderId = null;
        String logPrefix = "进入[" + ifCode + "]支付回调：urlOrderId：["
                + StringUtils.defaultIfEmpty(String.valueOf(urlOrderId), "") + "] ";
        log.info("===== {} =====", logPrefix);

        try {
            Long tenantId = payOrderService.getTenant(urlOrderId);
            TenantContextHolder.setTenantId(tenantId);
            //如果已经存在回调通知记录则跳过
            MchNotifyRecordDO notifyRecordDO = mchNotifyRecordService.findByPayOrder(urlOrderId);
            if (ObjectUtil.isNotNull(notifyRecordDO)) {
                log.info("notifyRecordDO exist");
                ChannelRetMsg channelResult = new ChannelRetMsg();
                JSONObject resJSON = new JSONObject();
                resJSON.put("code", "SUCCESS");
                resJSON.put("message", "成功");
                ResponseEntity okResponse = jsonResp(resJSON);
                //响应数据
                channelResult.setResponseEntity(okResponse);
                return channelResult.getResponseEntity();
            }
            // 参数有误
            if (StringUtils.isEmpty(ifCode)) {
                return ResponseEntity.badRequest().body("ifCode is empty");
            }

            //查询支付接口是否存在
            IChannelNoticeService payNotifyService =
                    SpringUtil.getBean(ifCode + "ChannelNoticeService", IChannelNoticeService.class);

            // 支付通道接口实现不存在
            if (payNotifyService == null) {
                log.error("{}, interface not exists ", logPrefix);
                return ResponseEntity.badRequest().body("[" + ifCode + "] interface not exists");
            }

            // 解析订单号 和 请求参数
            MutablePair<Long, Object> mutablePair =
                    payNotifyService.parseParams(request, urlOrderId,
                            IChannelNoticeService.NoticeTypeEnum.DO_NOTIFY);
            // 解析数据失败， 响应已处理
            if (mutablePair == null) {
                log.error("{}, mutablePair is null ", logPrefix);
                //需要实现类自行抛出ResponseException, 不应该在这抛此异常。
                throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST.getCode(),
                        "解析数据异常！");
            }

            //解析到订单号
            payOrderId = mutablePair.left;
            log.info("{}, 解析数据为：payOrderId:{}, params:{}",
                    logPrefix, payOrderId, mutablePair.getRight());

            if (ObjectUtil.isNotNull(urlOrderId) && !urlOrderId.equals(payOrderId)) {
                log.error("{}, 订单号不匹配. urlOrderId={}, payOrderId={} ", logPrefix, urlOrderId, payOrderId);
                throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST.getCode(),
                        "订单号不匹配！");
            }

            //获取订单号 和 订单数据
            PayOrderDO payOrder = payOrderService.getById(payOrderId);

            // 订单不存在
            if (payOrder == null) {
                log.error("{}, 订单不存在. payOrderId={} ", logPrefix, payOrderId);
                return payNotifyService.doNotifyOrderNotExists(request);
            }
            MchInfoDO mchInfoDO = mchInfoService.getMchInfoById(payOrder.getMchId());
            MchAppDO mchAppDO = mchAppService.getById(payOrder.getAppId());
            //查询出商户应用的配置信息
            MchAppConfigContext mchAppConfigContext =
                    configContextQueryService.queryMchInfoAndAppInfo(mchInfoDO.getId(),
                            mchAppDO.getId());


            //调起接口的回调判断
            ChannelRetMsg notifyResult = payNotifyService.doNotice(request, mutablePair.getRight(), payOrder, mchAppConfigContext, IChannelNoticeService.NoticeTypeEnum.DO_NOTIFY);
            log.info("notifyResult:{}", notifyResult);
            // 返回null 表明出现异常， 无需处理通知下游等操作。
            if (notifyResult == null || notifyResult.getChannelState() == null || notifyResult.getResponseEntity() == null) {
                log.error("{}, 处理回调事件异常  notifyResult data error, notifyResult ={} ", logPrefix, notifyResult);
                //需要实现类自行抛出ResponseException, 不应该在这抛此异常。
                throw new ServiceException(GlobalErrorCodeConstants.BAD_REQUEST.getCode(),
                        "处理回调事件异常！");
            }
            //默认更新成功
            boolean updateOrderSuccess = true;
            // 订单是 【支付中状态】
            if (payOrder.getState() == PayOrderConstant.STATE_ING) {

                //明确成功
                if (ChannelRetMsg.ChannelState.CONFIRM_SUCCESS == notifyResult.getChannelState()) {

                    updateOrderSuccess = payOrderService.updateIng2Success(payOrderId,
                            notifyResult.getChannelOrderId(), notifyResult.getChannelUserId());

                    //明确失败
                } else if (ChannelRetMsg.ChannelState.CONFIRM_FAIL == notifyResult.getChannelState()) {

                    updateOrderSuccess = payOrderService.updateIng2Fail(payOrderId, notifyResult.getChannelOrderId(), notifyResult.getChannelUserId(), notifyResult.getChannelErrCode(), notifyResult.getChannelErrMsg());
                }
            }

            // 更新订单 异常
            if (!updateOrderSuccess) {
                log.error("{}, updateOrderSuccess = {} ", logPrefix, updateOrderSuccess);
                return payNotifyService.doNotifyOrderStateUpdateFail(request);
            }

            //订单支付成功 其他业务逻辑
            if (notifyResult.getChannelState() == ChannelRetMsg.ChannelState.CONFIRM_SUCCESS) {
                payOrderProcessService.confirmSuccess(payOrder);
            }

            log.info("===== {}, 订单通知完成。 payOrderId={}, parseState = {} =====", logPrefix, payOrderId, notifyResult.getChannelState());

            return notifyResult.getResponseEntity();

        } catch (ServiceException e) {
            log.error("{}, payOrderId={}, BizException", logPrefix, payOrderId, e);
            return ResponseEntity.badRequest().body(e.getMessage());

        } catch (ResponseException e) {
            log.error("{}, payOrderId={}, ResponseException", logPrefix, payOrderId, e);
            return e.getResponseEntity();

        } catch (Exception e) {
            log.error("{}, payOrderId={}, 系统异常", logPrefix, payOrderId, e);
            return ResponseEntity.badRequest().body(e.getMessage());
        }
    }


    /*  跳转到支付成功页面 **/
    private String toReturnPage(String errInfo) {


        return "cashier/returnPage";
    }

    private void onlyJump(Long orderId, String logPrefix) throws IOException {

        if (ObjectUtil.isNotNull(orderId)) {
            //获取订单号 和 订单数据
            PayOrderDO payOrder = payOrderService.getById(orderId);

            // 订单不存在
            if (payOrder == null) {
                log.error("{}, 订单不存在. payOrderId={} ", logPrefix, orderId);
                this.toReturnPage("支付订单不存在");
            }
            MchInfoDO mchInfoDO = mchInfoService.getMchInfoById(payOrder.getMchId());
            MchAppDO mchAppDO = mchAppService.getById(payOrder.getAppId());
            //查询出商户应用的配置信息
            MchAppConfigContext mchAppConfigContext
                    = configContextQueryService.queryMchInfoAndAppInfo(mchInfoDO.getId(),
                    mchAppDO.getId());

            if (StringUtils.isBlank(payOrder.getReturnUrl())) {
                this.toReturnPage(null);
            }
            response.sendRedirect(payMchNotifyService.createReturnUrl(payOrder,
                    mchAppConfigContext.getMchApp().getAppSecret()));
        }
    }

    public static String getVerifyNotifyBody(HttpServletRequest request) {
        BufferedReader br = null;
        try {
            StringBuilder result = new StringBuilder();
            br = request.getReader();
            for (String line; (line = br.readLine()) != null; ) {
                if (result.length() > 0) {
                    result.append("\n");
                }
                result.append(line);
            }
            return result.toString();
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * json类型的响应数据
     **/
    protected ResponseEntity jsonResp(Object body) {
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setContentType(MediaType.APPLICATION_JSON);
        return new ResponseEntity(body, httpHeaders, HttpStatus.OK);
    }
}
